Skip to content

Conversation

@ksinghal
Copy link

@ksinghal ksinghal commented Oct 15, 2025

Summary

  • This is an initial effort towards implementing zod4 support.
  • Add Zod v4 monolithic integration for Convex in packages/convex-helpers/server/zod4.ts.
  • Strongly typed implementation (no loose any for public APIs); lints clean.

Key Changes

  • New API surface in server/zod4.ts: zid, zBrand, zCustomQuery/Mutation/Action, withSystemFields, zodToConvex, zodToConvexFields, zodOutputToConvex, convexToZod, toConvexJS, fromConvexJS, convexCodec.
  • Package export: add ./server/zod4 subpath (types + default) in packages/convex-helpers/package.json.

Documentation

  • Expanded Zod Validation section for v4 in packages/convex-helpers/README.md (see README for usage and examples).

Tests

  • Added smoke test packages/convex-helpers/server/zod4.test.ts (parity suite to follow).

Peer Dependency

  • Update zod peer range to ^3.22.4 || ^4.1.12 (v4.1+ required).

Compatibility

  • Existing v3 helper (server/zod) remains unchanged; new v4 functionality is opt-in via ./server/zod4.

Follow-ups

  • Port full v3 parity tests to zod4.test.ts.
  • Add an end-to-end example module using server/zod4.

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

Fixes #558
Fixes #793

@ksinghal
Copy link
Author

Huge credit to @panzacoder for his implementation in Zodvex, which this is heavily inspired by!!

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 15, 2025

Open in StackBlitz

npm i https://pkg.pr.new/get-convex/convex-helpers@818

commit: e69aae5

@ksinghal ksinghal marked this pull request as draft October 15, 2025 18:36
ExtraArgs
>,
) {
return customFnBuilder(query, customization) as any;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Casting any here would just override the types, no? Same for zCustomMuation and zCustomAction. Seems to be the case so far in my limited testing:

Image

Copy link

@natedunn natedunn Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Different spot but same idea?

CleanShot 2025-10-16 at 11  49 33

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very quick update before bed: re-adding CustomBuilder as seen in the v3 version, instead of any does seem to fix the issue related to the custom functions. We might need to generally review the any usage in order to preserve type-safety. I might have some time tomorrow to take a full pass on it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't have enough time to complete a full pass yet. But making some progress though my own adjustments and using a bit from @panzacoder's wonderful zodvex project, mostly helping me with the zodToConvex() feature. Hopefully that is ok, let me know if you have any objection.

@@ -0,0 +1,642 @@
import { z } from "zod";
Copy link
Member

@Nicolapps Nicolapps Oct 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import { z } from "zod";
import { z } from "zod/v4/core";

I think that this is change is necessary. With the current import, users of convex-helpers that use Zod 3 would get the /zod4 types incorrectly defined based on Zod 3 (see https://zod.dev/library-authors?id=how-to-support-zod-4). According to the same page, we need to import from zod/v4/core, unless we do something that can’t work with Zod Mini (but they strongly recommend against doing so).

@natedunn
Copy link

@ksinghal I have big overhaul to fix some issues I found in type-safety and that were addressed by @Nicolapps. It also introduces some further fixes provided by @panzacoder's Zodvex. Would you mind if I made some edits to this PR or should I create a fresh one since its a fairly sizable edit? I am ok either way, I just need to get v4 support shipped finally. 😅

@ksinghal
Copy link
Author

please go ahead and make the edits here if you like!

@panzacoder
Copy link

panzacoder commented Oct 21, 2025

@ksinghal I have big overhaul to fix some issues I found in type-safety and that were addressed by @Nicolapps. It also introduces some further fixes provided by @panzacoder's Zodvex. Would you mind if I made some edits to this PR or should I create a fresh one since its a fairly sizable edit? I am ok either way, I just need to get v4 support shipped finally. 😅

I'm also happy to contribute here. Don't mind that my code is being borrowed (obv it's OSS) but I probably would have opened a PR here already if I thought that was what the convex team wanted.

@natedunn if you've already put some work into it I'll wait for your edits, otherwise I can make some contributions later today / this week.

@natedunn
Copy link

natedunn commented Oct 24, 2025

@panzacoder Honestly my approach was just to edit this PR fairly minimally, but I ran into a few type issues that your approach seemed to solve already. Both of you guys had solid, sizable contributions. I'm just bringing them together and patching up a few things. 🙏

Was going to push changes up today but noticed that this PR isn't set up to accept edits. Mind checking that @ksinghal? Appreciate it

@ksinghal
Copy link
Author

Unbelievable - it seems I am unable to access the "allow maintainers to edit" box based on this discussion: https://github.com/orgs/community/discussions/5634. Can't believe that's the case so let me know if im just not looking in the right place.

@natedunn
Copy link

natedunn commented Oct 24, 2025

@ksinghal Yeah, I was curious about how that flow would even work. Given its a few years old and there doesn't seem to be a work around, I think I should create another PR? Why I hate that is it robs you of the PR but also still doesn't allow you or @panzacoder to submit edits. Only reviews. 🙄

I could also just send you my updates through my own fork too. But again, no flow for further edits. Annoying.

@panzacoder
Copy link

You could open a PR into this branch if you want to keep it focused here I think?

@ksinghal
Copy link
Author

Don't worry I don't care for the credit at all, I just want to help get it working. I am happy with whatever solution works for you!

@natedunn
Copy link

I will submit a PR to your fork. That should work. I'm currently flying so I'll get to that as soon as I have a few minutes free.

@Nicolapps
Copy link
Member

Hi everyone, thanks a lot for your contributions! I reviewed this PR and there are a few things I think we should fix before adding Zod 4 support to convex-helpers.

The most important change is that I think we should handle dates differently. This PR tries to automatically convert dates to/from v.float64(), which I think is a bad idea. When converting values automatically, it makes the behavior more complex to users. In particular, it’s not clear how z.union([z.date(), z.number()]) would be handled. This also requires users to use toConvexJS/fromConvexJS on the client, which doesn’t feel evident. Instead, Zod 4.1’s codecs API allows users to explicitly define how encoding/decoding should be done, which is ideal in this case. I wrote a design doc about how we can let users take advantage of codecs here: https://www.notion.so/convex-dev/Convex-Zod-4-Codecs-integration-294b57ff32ab809fa067fe63c740ca74?source=copy_link

A few other things on my mind:

  • We should target "zod": "^3.25.0 || ^4.0.0" in our peer dependencies as suggested by https://zod.dev/library-authors, which will make sure that we can target both Zod 3 and Zod 4 at the same time. And inside of zod4.ts, we should import from "zod/v4/core", as recommended by the same page. You can find more details about how we can handle Zod 3 + 4 in convex-helpers in my design doc.
  • I think that the implementation of zid is more complex than it could be. In particular, I don’t think that we should use Zod branded values here, since Convex already uses its own branded string type for IDs. Using a Zod branded value would meant that the output type of Zod woudn’t be compatible with the Id type from Convex, which sounds incorrect.
  • What is the goal of checking for visited types in zodToConvexInternal? Is it to support self-referential types, as in https://zod.dev/api?id=recursive-objects ? If so, it could be useful to add a comment + a test
  • I found some things that I believe are bugs in this PR’s implementation:
    • Why is the code sometimes trying to remove falsy elements from arrays (e.g. members.filter((m): m is GenericValidator => !!m))? This looks suspicious, because this is done on arrays where the elements should always be truthy.
    • An empty union type means that the value can never exist (z.union([]) is equivalent to never in TypeScript). So z.union([]) should be converted to v.union(), not v.any().
    • When evaluating z.ZodLazy, I think that errors thrown by the schema should propagate, not be caught and ignored.
    • The handling of records looks incorrect: we shouldn’t assume that the key is v.string(), because it could also be a union of litterals. Also, in Zod 4, we must handle partial records differently.

I would be happy to write a PR with these changes. Thanks again for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Zid can't be in a z.array Update zod validation to support Zod 4

4 participants